/*
This file is part of Docking@Home.
http://docking.gcl.cis.udel.edu
Copyright (C) 2009 University of Delaware

Docking@Home is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Docking@Home is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Docking@Home.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
	Filename: BioModel.cpp
	Description: A abstract class that will contain all information and 
		functions that any biological model (protein or ligand) in our case 
		will need to be displayed
	Revision History: 
		Created - 01/04/08
	Author: Robert Keller (01/04/08 -> Current)
*/
#include "BioModel.h"
#include "../Math/VectorMath.h"
#include "../Math/TrigMath.h"

#include "parse.h"
#include "gutil.h"
#include "boinc_gl.h"
#include "graphics_api.h"
#include "txf_util.h"

// ------------------------------------------------------------
// Static Variable Declarations
// ------------------------------------------------------------

GLHelper * BioModel::glHelper;

// ------------------------------------------------------------
// Constructor / Destructor
// ------------------------------------------------------------

BioModel::BioModel ()
	: DEFAULT_LINE_LENGTH(15.0f),
	  DEFAULT_CPK_LENGTH(20.0f),
	  DEFAULT_VDW_LENGTH(10.0f),
	  DEFAULT_MULTIPLE_LENGTH(15.0f),
	  DEFAULT_CPK_ATOM_SIZE(7.0f)
{
	translateX = 0.0f;
	translateY = 0.0f;
	translateZ = 0.0f;

	pdbLastModified.st_mtime = 0;
	psfLastModified.st_mtime = 0;

	userRotation = 0.0f;
	modelRotation = 0.0f;
	modelRotationAxis[0] = 0.0f;
	modelRotationAxis[1] = 0.0f;
	modelRotationAxis[2] = 0.0f;

	maxEner = 0.0f;
	rmsd = 0.0f;
	seed = 0.0f;

	Center[0]=Center[1]=Center[2]=0.f;
}// BioModel

BioModel::~BioModel ()
{
	atoms.clear();
	bonds.clear();
}// ~BioModel

// ------------------------------------------------------------
// Public Member Modifiers
// ------------------------------------------------------------

void BioModel::setPDBFilename (const char filename[BIOMODEL_MAX_FILNAME_LENGTH])
{
	memcpy(pdbFilename, filename, BIOMODEL_MAX_FILNAME_LENGTH * sizeof(char));
	pdbLastModified.st_mtime = 0;
}// setPDBFilename

void BioModel::setPSFFilename (const char filename[BIOMODEL_MAX_FILNAME_LENGTH])
{
	memcpy(psfFilename, filename, BIOMODEL_MAX_FILNAME_LENGTH * sizeof(char));
	psfLastModified.st_mtime = 0;
}// setPSFFilename

// ------------------------------------------------------------
// Public Member Accessors
// ------------------------------------------------------------

ArcBallT * BioModel::getArcBall ()
{
	return &arcBall;
}// getArcBall

unsigned int BioModel::getNumAtoms ()
{
	return atoms.size();
}// getNumAtoms

unsigned int BioModel::getNumBonds ()
{
	return bonds.size();
}// getNumBonds

// ------------------------------------------------------------
// Protected File Reading Functions
// ------------------------------------------------------------

bool BioModel::readFile ()
{
	// Resolved filenames
	char pdb_resolved_name[256]; 
	char psf_resolved_name[256];

	// File information
	struct stat pdbFileInfo;
	struct stat psfFileInfo;

	// resolve .pdb file
	if (boinc_resolve_filename(pdbFilename,	pdb_resolved_name, sizeof(pdb_resolved_name)))
	{
		fprintf(stderr, "Error resolving pdb file: ");
		fprintf(stderr, "%s", pdbFilename);
		fprintf(stderr, "\n");
	}// if
	
	// resolve .psf file
	if (boinc_resolve_filename(psfFilename, psf_resolved_name, sizeof(psf_resolved_name)))
	{
		fprintf(stderr, "Error resolving psf file: ");
		fprintf(stderr, "%s", psfFilename);
		fprintf(stderr, "\n");
	}

	// Get file information
	stat(pdb_resolved_name, &pdbFileInfo);		
	stat(psf_resolved_name, &psfFileInfo);	

	// Check last modified to see if it needs to be parsed
	if (pdbLastModified.st_mtime == pdbFileInfo.st_mtime && psfLastModified.st_mtime == psfFileInfo.st_mtime)
		return false;

	// Update when the files were last modified
	pdbLastModified.st_mtime = pdbFileInfo.st_mtime;
	psfLastModified.st_mtime = psfFileInfo.st_mtime;

	if (!atoms.empty())
		atoms.clear();

	if (!bonds.empty())
		bonds.clear();

	// Read the files
	readPDB(pdb_resolved_name);
	readPSF(psf_resolved_name);

	//get the Center of the molecule
	this->calculateModelCenter(this->Center);

	return true;

}// readFile

// ------------------------------------------------------------
// Rotation Functions
// ------------------------------------------------------------

void BioModel::addUserRotation (float deg)
{
	userRotation += deg;

	if (userRotation > 180.0f)
		userRotation -= 360.0f;

}// addUserRotation

void BioModel::subtractUserRotation (float deg)
{
	userRotation -= deg;

	if (userRotation < -180.0f)
		userRotation += 360.0f;

}// subtractUserRotation

// ------------------------------------------------------------
// Draw Representation Functions
// ------------------------------------------------------------

DrawRep BioModel::getDrawRep ()
{
	return drawRep;
}// getDrawRep

float BioModel::getDistanceBetweenFromDrawRep ()
{
	switch (drawRep)
	{
	case VDW:
		return DEFAULT_VDW_LENGTH;
	case CPK:
		return DEFAULT_CPK_LENGTH;
	case LINE:
		return DEFAULT_LINE_LENGTH;
	}// switch

	return 0.0f;

}// getDistanceBetweenFromDrawRep 

void BioModel::setDrawRep (DrawRep rep)
{
	drawRep = rep;
}// setDrawRep

// ------------------------------------------------------------
// No Atoms Rendering Functions
// ------------------------------------------------------------

void BioModel::drawNoAtomsText (float winWidth, float winHeight)
{	
	// Text Properties
	int width; 
	int max_ascent; 
	int max_descent;

	// Center of the drawing window
	float winCenterX;
	float winCenterY;

	// Scale and Shifting of the Resulting Text
	float scale;
	float xShift;
	float yShift;

	char str[21] = "No Model Formed Yet.";

	glHelper->setOrthographic();

	winCenterX = winWidth / 2.0f;
	winCenterY = winHeight / 2.0f;

	glHelper->getDrawnTextProperties(str, 0, &width, 
		&max_ascent, &max_descent);

	// Set the scale based on the width based on a padding 
	// of 50 px
	scale = winWidth / (width + 50.0f);

	// Make sure the height is not the issue based on a padding
	// of 50 px
	if (winHeight / (max_ascent + max_descent + 50.0f) < scale)
		scale = winHeight / (max_ascent + max_descent + 50.0f);

	xShift = winCenterX - scale * width / 2.0f;
	yShift = winCenterY + scale * 
		(max_ascent + max_descent) / 2.0f;

	float color[4];

	glHelper->copyColor4f(color, glHelper->WHITE);

	glHelper->drawText(0.1f, xShift, yShift, 1.0f, 
		scale, -scale, 1.0f, color, 0, str);

	glHelper->setOrthographicDone();
}// drawNoAtomsText

// ------------------------------------------------------------
// Protected Member Modifiers
// ------------------------------------------------------------

void BioModel::setMaxEner(float ener)
{
	maxEner = ener;
}// setMaxEner

void BioModel::setRmsd(float newRmsd)
{
	rmsd = newRmsd;
}// setRmsd

void BioModel::setSeed(float newSeed)
{
	seed = newSeed;
}// setSeed

// ------------------------------------------------------------
// Member Accessors
// ------------------------------------------------------------

Atom BioModel::getAtom (unsigned int index)
{
	return atoms[index];
}// getAtom

Bond BioModel::getBond (unsigned int index)
{
	return bonds[index];
}// getBond

float BioModel::getMaxEner () const 
{
	return maxEner;
}// getMaxEner

float BioModel::getRMSD () const 
{
	return rmsd;
}// getRMSD

float BioModel::getSeed () const 
{
	return seed;
}// getSeed

// ------------------------------------------------------------
// Atom / Bond Addition Functions
// ------------------------------------------------------------

void BioModel::addAtom (Atom a)
{
	atoms.push_back(a);
}// addAtom

void BioModel::addBond (Bond b)
{
	bonds.push_back(b);
}// addBond

// ------------------------------------------------------------
// Protected File Reading Functions
// ------------------------------------------------------------

// See http://deposit.rcsb.org/adit/docs/pdb_atom_format.html
// and http://bmerc-www.bu.edu/needle-doc/latest/atom-format.html#pdb-atom-name-anomalies
// for details on the pdb atom format and how it is stored
void BioModel::readPDB (char resolved_pdbFilename [BIOMODEL_MAX_FILNAME_LENGTH])
{
	FILE *inputFile; // pointer to input file

	// Temporary storage variables

	char line[100]; // One line of text
	char substring[9]; // Substring from the line
	Atom tempAtom;
	Bond tempBond;
	int loc = 0;

	inputFile = fopen(resolved_pdbFilename, "r"); 

	if (inputFile == NULL)
		return;

	// read a line at a time until no more exist
	while(fgets(line, 100, inputFile) != NULL)
	{ 	
		if (line[0] != '\n')
		{
			strncpy(substring, line, 6);
			substring[6] = '\0';
			loc += 6;

			if (strcmp(substring, "ATOM  ") == 0)
			{	
				// ID Number
				strncpy(substring, line+loc, 5);
				substring[5] = '\0';
				loc += 6;
				tempAtom.setId(atoi(substring));

				// Get Atom Name and skip next few fields since 
				// they are unused
				strncpy(substring, line+loc, 4);
				substring[4] = '\0';
				loc += 18;
				tempAtom.setName(substring);				
				tempAtom.setColorID();

				// X Coordinate
				strncpy(substring, line+loc, 8);
				substring[8] = '\0';
				loc += 8;
				tempAtom.setXCoord((float)atof(substring));	

				// Y Coordinate
				strncpy(substring, line+loc, 8);
				substring[8] = '\0';
				loc += 8;
				tempAtom.setYCoord((float)atof(substring));	

				// Z Coordinate
				strncpy(substring, line+loc, 8);
				substring[8] = '\0';
				loc += 8;
				tempAtom.setZCoord((float)atof(substring));	

				addAtom(tempAtom);

				/* Old Way that did not take into account when
					white space had a effect on the meaning of
					some of the elements stored in the pdb file

				token = strtok(NULL, " "); // ID number
				tempAtom.setId(atoi(token));
				token = strtok(NULL, " "); // name
				strncpy(name, token, 3);
				tempAtom.setName(name);
				tempAtom.setColorID();
				token = strtok(NULL, " "); // ligand-name
				token = strtok(NULL, " "); // residue seq #
				token = strtok(NULL, " "); // get x-coordinate
				tempAtom.setXCoord((float)atof(token));
				token = strtok(NULL, " "); // get y-coordinate
				tempAtom.setYCoord((float)atof(token));
				token = strtok(NULL, " "); // get z-coordinate
				tempAtom.setZCoord((float)atof(token));
				addAtom(tempAtom);
				*/

			}// if

			loc = 0;
		}// if
	}// while

	fclose(inputFile);

}// readPDB

void BioModel::readPSF (char resolved_psfFilename [BIOMODEL_MAX_FILNAME_LENGTH])
{
	FILE *inputFile; // pointer to input file

	// Temporary storage variables

	char line[100]; // One line of text
	char *token; // String token
	Atom tempAtom;
	Bond tempBond;	

	int i = 0;
	int col = 0;

	inputFile = fopen(resolved_psfFilename,"r");
	
	if (inputFile == NULL)
		return;

	// Read a line at a time until none exist
	while(fgets(line, 100, inputFile) != NULL)
	{
		token = strtok(line, " ");
		if (token[0]=='*' && token[1] != '*')
		{
			token = strtok(NULL, " ");

			// read the max energy
			if (strcmp(token,"MAXENER")==0)
			{ 
				token = strtok(NULL, " ");
				maxEner = (float)atof(token);
			}
			// read the max rmsd
			else if (strcmp(token,"MAXRMSD")==0)
			{ 
				token = strtok(NULL, " ");
				rmsd = (float)atof(token);
			}
			// read the rmsd
			else if (strcmp(token,"RMSD")==0)
			{ 
				token = strtok(NULL, " ");
				rmsd = (float)atof(token);
			}
			// read the seed
			else if (strcmp(token, "SEED")==0)
			{ 
				token = strtok(NULL, " ");
				seed = (float)atof(token);
			}// else if
			// Gets current rotation amount and axis
			else if (strcmp(token, "ROTATION") == 0)
			{
				// NUMBER
				token = strtok(NULL, " ");
				// The rotation number
				token = strtok(NULL, " ");
				// Actual degree amount
				token = strtok(NULL, " ");
				modelRotation = (float)atof(token);
				// DEGREES
				token = strtok(NULL, " ");
			}// else if
			// Gets Axis of Rotation
			else if (strcmp(token, "AXIS") == 0)
			{
				token = strtok(NULL, " ");
				modelRotationAxis[0] = (float)atof(token);

				token = strtok(NULL, " ");
				modelRotationAxis[1] = (float)atof(token);

				token = strtok(NULL, " ");
				modelRotationAxis[2] = (float)atof(token);
			}// else if
		}// if 
		else 
		{
			char a = token[0];
			if (isdigit(a) != 0)
			{
				unsigned int numLines = atoi(token);
				
				token = strtok(NULL, " ");
				// Read additional atom information
				if (strcmp("!NATOM\n", token) == 0)
				{						
					for(unsigned int i = 0; i < numLines; i++)
					{
						if (fgets(line, 100, inputFile)!=NULL)
						{
							token = strtok(line, " ");

							// Make sure both files have atoms in same order
							if (atoms[i].getId() == atoi(token))
							{
								for(col = 1; col <= 7; col++)
									token = strtok(NULL, " ");

								// read atom mass
								atoms[i].setMass((float)atof(token));	
							}// if
						}// if 
					}// for
				}// if
				else
				{	
					// Read bond information
					if(strcmp("!NBOND:", token) == 0)
					{ 
						for(int i = numLines; i > 0; i -= 4)
						{
							if (fgets(line, 100, inputFile) != NULL)
							{
								token = strtok(line, " "); // atom 1
								tempBond.setAtom1(atoi(token) - 1);
								token = strtok(NULL, " "); // atom 2
								tempBond.setAtom2(atoi(token) - 1);

								addBond(tempBond);

								for (int j = 1; j <= 3 && j < i; j++)
								{
									token = strtok(NULL, " "); // atom 1
									tempBond.setAtom1(atoi(token) - 1);
									token = strtok(NULL, " "); // atom 2
									tempBond.setAtom2(atoi(token) - 1);

									addBond(tempBond);
								}// for
							}// if
						}// for

						break;
					}// if
				}// else
			}// if
		}// else
	}// while
	int asfd = bonds.capacity();

	fclose(inputFile);

}// readPSF

// ------------------------------------------------------------
// Rendering Representations
// ------------------------------------------------------------

// Name: drawVDWRep
// Description: Draws the model as a set of connected atoms that
//		are represented as spheres whose size is based upon the 
//		mass of the atom

void BioModel::drawVDWRep ()
{
	drawVDWRep (DEFAULT_VDW_LENGTH);
}// drawVDWRep

// Name: drawVDWRep
// Description: Draws the model as a set of connected atoms that
//		are represented as spheres whose size is based upon the 
//		mass of the atom
// Parameters: 
//		distanceBetween, the distance between each atom (in this case it is
//			how long the line is that represents the bond)

void BioModel::drawVDWRep (float distanceBetween)
{
	float color[4]; // color of the atom 
	int colorID; // Atom's color id
	float atomPos[3]; // Position of an atom

	for(unsigned int i = 0; i < getNumAtoms(); i++)
	{  // get atom coordinates
		Atom tempAtom = getAtom(i);

		atomPos[0] = (tempAtom.getXCoord() ) * distanceBetween;
		atomPos[1] = (tempAtom.getYCoord() ) * distanceBetween;
		atomPos[2] = (tempAtom.getZCoord() ) * distanceBetween;

		colorID = tempAtom.getColorID(); // set color for atom 
		
		if (colorID == WHITE_ID)
			tempAtom.setMass(5.0f);

		glHelper->getColorFromID(color, colorID);	
		mode_shaded(color);	

		drawSphere(atomPos, tempAtom.getMass()); // draw atom
	}// for

	mode_unshaded();

}// drawVDWRep

// Name: drawLineRep
// Description: Draws the model as a set of bonds of different colored lines
//		where each color represents a different type of atom

void BioModel::drawLineRep ()
{
	drawLineRep (DEFAULT_LINE_LENGTH);
}// drawLineRep

// Name: drawLineRep
// Description: Draws the model as a set of bonds of different colored lines
//		where each color represents a different type of atom
// Parameters: 
//		distanceBetween, the distance between each atom (in this case it is
//			how long the line is that represents the bond)

void BioModel::drawLineRep (float distanceBetween)
{
	float pos1[3]; // starting point of a line
	float pos2[3]; // end point of a line
	float midpoint[3]; // midpoint of a line
	float color[4]; // color of the atom 
	int colorID; // Atom's color id

	mode_lines();

	for(unsigned int i = 0; i < getNumBonds(); i++)
	{  // get bond coordinates
		int a1 = getBond(i).getAtom1();
		int a2 = getBond(i).getAtom2();

		pos1[0] = (getAtom(a1).getXCoord() ) * distanceBetween;
		pos1[1] = (getAtom(a1).getYCoord() ) * distanceBetween;
		pos1[2] = (getAtom(a1).getZCoord() ) * distanceBetween;

		pos2[0] = (getAtom(a2).getXCoord() ) * distanceBetween;
		pos2[1] = (getAtom(a2).getYCoord() ) * distanceBetween;
		pos2[2] = (getAtom(a2).getZCoord() ) * distanceBetween;

		midpoint[0] = (pos1[0] + pos2[0]) / 2.0f;		
		midpoint[1] = (pos1[1] + pos2[1]) / 2.0f;
		midpoint[2] = (pos1[2] + pos2[2]) / 2.0f;

		colorID = getAtom(a1).getColorID(); // set color for atom 
		glHelper->getColorFromID(color, colorID);

		glHelper->drawLine(pos1, midpoint, color);
		
		colorID = getAtom(a2).getColorID(); // set color for atom 
		glHelper->getColorFromID(color, colorID);

		glHelper->drawLine(midpoint, pos2, color);
	}// for
	
	mode_unshaded();

}// drawLineRep

// Name: drawCPKRep
// Description: Draws the model as atoms of equal size colored based 
//		upon what type of atom they are and each atom is connected by
//		a series of bonds that are represented as colored cylinders

void BioModel::drawCPKRep ()
{
	drawCPKRep (DEFAULT_CPK_LENGTH);
}// drawCPKRep

// Name: drawCPKRep
// Description: Draws the model as atoms of equal size colored based 
//		upon what type of atom they are and each atom is connected by
//		a series of bonds that are represented as colored cylinders
// Parameters: 
//		distanceBetween, the distance between each atom (in this case it is
//			how long the line is that represents the bond)

void BioModel::drawCPKRep (float distanceBetween)
{
	unsigned int i;
	float atomPos[3]; // location of an atom
	float bondPos1[3]; // bond starting location
	float bondPos2[3]; // bond ending locations
	float color[4]; // color of the atom 
	int colorID; // Atom's color id

	for(i = 0; i < getNumAtoms(); i++)
	{  // get atom coordinates
		atomPos[0] = (getAtom(i).getXCoord() ) * distanceBetween;
		atomPos[1] = (getAtom(i).getYCoord() ) * distanceBetween;
		atomPos[2] = (getAtom(i).getZCoord() ) * distanceBetween;

		colorID = getAtom(i).getColorID(); // set color for atom 
		glHelper->getColorFromID(color, colorID);
		mode_shaded(color);	

		drawSphere(atomPos, DEFAULT_CPK_ATOM_SIZE); // draw atom
	}// for	

	for (i = 0; i < getNumBonds(); i++)
	{
		int a1 = getBond(i).getAtom1();
		int a2 = getBond(i).getAtom2();

		bondPos1[0] = getAtom(a1).getXCoord() * distanceBetween;
		bondPos1[1] = getAtom(a1).getYCoord() * distanceBetween;
		bondPos1[2] = getAtom(a1).getZCoord() * distanceBetween;

		bondPos2[0] = getAtom(a2).getXCoord() * distanceBetween;
		bondPos2[1] = getAtom(a2).getYCoord() * distanceBetween;
		bondPos2[2] = getAtom(a2).getZCoord() * distanceBetween;

		float dist = distanceBetweenVectors(bondPos1, bondPos2) / 1.98f;

		GLfloat posSub [3];
		vectorSubtraction(bondPos2, bondPos1, posSub);

		vectorNormalize(posSub);
		
		float zAxis[3];
		zAxis[0] = 0;
		zAxis[1] = 0;
		zAxis[2] = 1.0f;

		float angle = (float)radiansToDegress(
			acos(dotProduct(zAxis, posSub)));

		float axis [3];
		crossProductNormalized(zAxis, posSub, axis);

		colorID = getAtom(a1).getColorID(); // set color for atom 
		glHelper->getColorFromID(color, colorID);
		mode_shaded(color);

		glPushMatrix();
		glHelper->drawCylinder(bondPos1, dist, 2.0f, (int)angle, axis);
		glPopMatrix();

		vectorSubtraction(bondPos1, bondPos2, posSub);
		vectorNormalize(posSub);
		angle = acos(dotProduct(zAxis, posSub)) / 3.141593f * 180.0f;
		crossProductNormalized(zAxis, posSub, axis);

		colorID = getAtom(a2).getColorID(); // set color for atom 
		glHelper->getColorFromID(color, colorID);
		mode_shaded(color);

		glPushMatrix();
		glHelper->drawCylinder(bondPos2, dist, 2.0f, (int)angle, axis);
		glPopMatrix();
	
	}// for

	mode_unshaded();
}// drawCPKRep

// ------------------------------------------------------------
// Rendering Helper Functions
// ------------------------------------------------------------

// Name: calculateModelCenter
// Description: Determines the bounding box of the biological
//		model in object space and determines it center
// Returns: modelCenter, the center of the model in object space

void BioModel::calculateModelCenter(float modelCenter[3])
{
	unsigned int i = 0;

	if (getNumAtoms() < 1)
	{
		modelCenter[0] = 0.0f;
		modelCenter[1] = 0.0f;
		modelCenter[2] = 0.0f;
		return;
	}

	float minX = getAtom(i).getXCoord();
	float maxX = getAtom(i).getXCoord();
	float minY = getAtom(i).getYCoord();
	float maxY = getAtom(i).getYCoord();
	float minZ = getAtom(i).getZCoord();
	float maxZ = getAtom(i).getZCoord();
	
	for(i = 1; i < getNumAtoms(); i++)
	{
		// find min and max for x, y and z
		if (getAtom(i).getXCoord() < minX){ 
			minX = getAtom(i).getXCoord();
		}
		if (getAtom(i).getXCoord() > maxX){
			maxX = getAtom(i).getXCoord();
		}
		if (getAtom(i).getYCoord() < minY){
			minY = getAtom(i).getYCoord();
		}
		if (getAtom(i).getYCoord() > maxY){
			maxY = getAtom(i).getYCoord();
		}
		if (getAtom(i).getZCoord() < minZ){
			minZ = getAtom(i).getZCoord();
		}
		if (getAtom(i).getZCoord() > maxZ){
			maxZ = getAtom(i).getZCoord();
		}
	}// for

	// calculate the center of the model
	modelCenter[0] = (minX + (maxX-minX)/2);
	modelCenter[1] = (minY + (maxY-minY)/2);
	modelCenter[2] = (minZ + (maxZ-minZ)/2);

}// calculateModelCenter

// Name: calculateTranslation
// Description: Calculates the translation for this model based
//		upon the objects center and the distance between the
//		atoms. This will move the model so that it is centered 
//		around 0,0,0.
// Returns: translation, the final translation amount

void BioModel::calculateCenteringTranslation(float centering[3],
	float modelCenter[3], float distanceBetween)
{
	centering[0] = -(modelCenter[0] * distanceBetween);
	centering[1] = -(modelCenter[1] * distanceBetween);
	centering[2] = -(modelCenter[2] * distanceBetween);
}// calculateTranslation

void BioModel::performModelTransformations (float distanceBetween,
	float angle, float trans[3], float arcTransformation[16] )
{
	float centering[3]; // tranlation vector

	glMatrixMode(GL_MODELVIEW);  

	glTranslatef(trans[0], trans[1], trans[2]);

	glMultMatrixf(arcTransformation);

	glRotatef(angle, 1.0, 1.0, 1.0); 

	glRotatef(-modelRotation, modelRotationAxis[0],
		modelRotationAxis[1],
		modelRotationAxis[2]);

	calculateCenteringTranslation(centering, this->Center, 
		distanceBetween); 
	
	// Translates model to the center of the screen
	glTranslatef(centering[0], centering[1], centering[2]); 

}// performModelTransformations
